package cn.icanci.loopstack.rec.admin.biz.service.impl;

import cn.hutool.json.JSONUtil;
import cn.icanci.loopstack.rec.admin.biz.mapper.config.BaseDataMapper;
import cn.icanci.loopstack.rec.admin.biz.service.BaseDataService;
import cn.icanci.loopstack.rec.admin.biz.event.log.LogEvent;
import cn.icanci.loopstack.rec.admin.biz.model.BaseDataDebugResult;
import cn.icanci.loopstack.rec.admin.dal.mongodb.common.PageList;
import cn.icanci.loopstack.rec.admin.dal.mongodb.daointerface.BaseDataDAO;
import cn.icanci.loopstack.rec.admin.dal.mongodb.daointerface.MetadataDAO;
import cn.icanci.loopstack.rec.admin.dal.mongodb.dateobject.BaseDataDO;
import cn.icanci.loopstack.rec.admin.dal.mongodb.dateobject.MetadataDO;
import cn.icanci.loopstack.rec.common.enums.LogOperatorTypeEnum;
import cn.icanci.loopstack.rec.common.enums.ModuleTypeEnum;
import cn.icanci.loopstack.rec.common.enums.ResultTypeEnum;
import cn.icanci.loopstack.rec.common.model.TextValue;
import cn.icanci.loopstack.rec.common.model.config.BaseDataVO;
import cn.icanci.loopstack.rec.engine.script.RecScriptEngine;
import cn.icanci.loopstack.rec.engine.script.RecScriptEngineManager;
import cn.icanci.loopstack.rec.engine.script.context.RecScriptEngineContext;
import cn.icanci.loopstack.rec.engine.script.enums.ResultTypeMapEnum;
import cn.icanci.loopstack.rec.spi.event.EventDispatcher;

import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.script.Bindings;
import javax.script.SimpleBindings;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.google.common.collect.Lists;

/**
 * @author icanci
 * @since 1.0 Created in 2022/11/12 08:58
 */
@Service
public class BaseDataServiceImpl implements BaseDataService {
    @Resource
    private BaseDataDAO     baseDataDAO;
    @Resource
    private MetadataDAO     metadataDAO;
    @Resource
    private BaseDataMapper  baseDataMapper;
    @Resource
    private EventDispatcher eventDispatcher;

    private final RecScriptEngine recScriptEngine = RecScriptEngineManager.getRecScriptEngine();

    @Override
    public List<BaseDataVO> queryAll() {
        return baseDataMapper.dos2vos(baseDataDAO.queryAll());
    }

    @Override
    public void save(BaseDataVO baseData) {
        if (doInsert(baseData)) {
            BaseDataDO insert = baseDataMapper.vo2do(baseData);
            baseDataDAO.insert(insert);
            eventDispatcher.fire(new LogEvent(insert.getUuid(), ModuleTypeEnum.REC_BASE_DATA, JSONUtil.toJsonStr(insert), LogOperatorTypeEnum.CREATE));
        } else {
            baseDataDAO.update(baseDataMapper.vo2do(baseData));
            eventDispatcher.fire(new LogEvent(baseData.getUuid(), ModuleTypeEnum.REC_BASE_DATA, JSONUtil.toJsonStr(baseData), LogOperatorTypeEnum.UPDATE));
        }
    }

    @Override
    public BaseDataVO queryById(String id) {
        return baseDataMapper.do2vo(baseDataDAO.queryOneById(id));
    }

    @Override
    public PageList<BaseDataVO> queryPage(BaseDataVO baseData, int pageNum, int pageSize) {
        PageList<BaseDataDO> pageQuery = baseDataDAO.pageQuery(baseDataMapper.vo2do(baseData), pageNum, pageSize);
        return new PageList<>(baseDataMapper.dos2vos(pageQuery.getData()), pageQuery.getPaginator());
    }

    @Override
    public BaseDataVO queryByFieldNameAndDomainCode(String fieldName, String domainCode) {
        return baseDataMapper.do2vo(baseDataDAO.queryByFieldNameAndDomainCode(fieldName, domainCode));
    }

    @Override
    public List<TextValue> loadSelector(String domainCode) {
        List<BaseDataVO> baseDatas = baseDataMapper.dos2vos(baseDataDAO.queryByDomainCode(domainCode));
        if (CollectionUtils.isEmpty(baseDatas)) {
            return Lists.newArrayList();
        }
        List<TextValue> textValues = Lists.newArrayList();
        for (BaseDataVO baseData : baseDatas) {
            String label;
            if (isDeleted(baseData)) {
                label = String.format(DELETED_FORMAT, baseData.getFieldName());
            } else {
                label = String.format(NOT_DELETED_FORMAT, baseData.getFieldName());
            }
            String value = baseData.getUuid();
            MetadataDO metadata = metadataDAO.queryByUuId(baseData.getMetadataUuid());
            List<MetadataDO.MetadataPair> metadataPairs = Lists.newArrayList();

            if (metadata != null) {
                metadataPairs.addAll(metadata.getMetadataPairs());
            }
            textValues.add(new TextValue(label, value, baseData.getDataType().getCode(), metadataPairs));
        }
        return textValues;
    }

    @Override
    public BaseDataDebugResult debug(BaseDataVO baseData, String scriptContentTest) {
        RecScriptEngineContext context;
        ResultTypeEnum resultType = baseData.getResultType();
        Bindings bindings = new SimpleBindings(JSONUtil.toBean(scriptContentTest, Map.class));
        if (resultType != null) {
            Class<?> clazz = ResultTypeMapEnum.getClassByResultType(resultType);
            context = recScriptEngine.eval(baseData.getScriptType(), bindings, baseData.getScriptContent(), clazz);
        } else {
            context = recScriptEngine.eval(baseData.getScriptType(), bindings, baseData.getScriptContent());
        }
        Object realRetVal = context.getRealRetVal();

        BaseDataDebugResult result = new BaseDataDebugResult();
        result.setReqResultType(baseData.getResultType() == null ? "无" : baseData.getResultType().getDesc());
        result.setScriptType(baseData.getScriptType().getDesc());
        result.setSuccess(realRetVal != null);
        result.setRealResult(String.valueOf(realRetVal));
        result.setExceptionMessage(context.getThrowable() == null ? StringUtils.EMPTY : context.getThrowable().getMessage());
        return result;
    }

    @Override
    public BaseDataVO baseDataQueryByUuid(String uuid) {
        return baseDataMapper.do2vo(baseDataDAO.baseDataQueryByUuid(uuid));
    }
}
